package com.hy.frame.util;

import android.util.LruCache;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;

@SuppressWarnings({"unused"})
public class ReflectUtil {

    private static final LruCache<String, Class<?>> mObjectLruCache = new LruCache<>(64);
    //未引入的类
    private static final Set<String> mObjectSet = new HashSet<>();

    static Field findField(Class<?> clazz, String... fieldNames) {
        if (fieldNames != null && fieldNames.length > 0) {
            for (String fieldName : fieldNames) {
                try {
                    return clazz.getDeclaredField(fieldName);
                } catch (NoSuchFieldException ignored) {
                }
            }
        }
        return null;
    }

    @SuppressWarnings({"unchecked", "unused"})
    public static <T> T invokeField(Object obj, Class<?> clazz, String... fieldName) {
        if (obj != null && clazz != null) {
            Field field = findField(clazz, fieldName);
            if (field != null) {
                try {
                    field.setAccessible(true);
                    return (T) field.get(obj);
                } catch (Exception ignored) {
                }
            }
        }
        return null;
    }

    public static <T> T invokeField(Object obj, String[] classNames, String... fieldName) {
        return invokeField(obj, classFromNames(classNames), fieldName);
    }

    @SuppressWarnings({"unchecked", "unused"})
    public static <T> T invokeMethod(Object obj, String methodName, Object... args) {
        Class<?>[] argsClass = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            argsClass[i] = args[i].getClass();
        }
        Method method = getMethod(obj.getClass(), methodName, argsClass);
        if (method != null) {
            try {
                return (T) method.invoke(obj, args);
            } catch (Exception e) {
                // Ignored
            }
        }
        return null;
    }

    @SuppressWarnings({"unchecked", "unused"})
    public static <T> T invokeStaticMethod(Class<?> clazz, String methodName, Object... args) {
        Class<?>[] argsClass = new Class[args.length];
        for (int i = 0; i < args.length; i++) {
            argsClass[i] = args[i].getClass();
        }
        if (clazz != null) {
            Method method = getMethod(clazz, methodName, argsClass);
            if (method != null) {
                try {
                    return (T) method.invoke(null, args);
                } catch (Exception e) {
                    // Ignored
                }
            }
        }
        return null;
    }

    static Method getMethod(Class<?> clazz, String methodName, Class<?>... params) {
        try {
            return clazz.getMethod(methodName, params);
        } catch (NoSuchMethodException e) {
            return null;
        }
    }

    @SuppressWarnings({"unused"})
    static Method getDeclaredRecur(Class<?> clazz, String methodName, Class<?>... params) {
        while (clazz != null && clazz != Object.class) {
            try {
                return clazz.getDeclaredMethod(methodName, params);
            } catch (NoSuchMethodException e) {
                clazz = clazz.getSuperclass();
            }
        }
        return null;
    }

    static Field findFieldObjRecur(Class<?> current, String fieldName) {
        while (current != null && current != Object.class) {
            try {
                Field field = current.getDeclaredField(fieldName);
                field.setAccessible(true);
                return field;
            } catch (NoSuchFieldException e) {
                current = current.getSuperclass();
            }
        }
        return null;
    }

    @SuppressWarnings({"unchecked", "unused"})
    static <T> T invokeFieldRecur(Object obj, String fieldName) {
        Field field = findFieldObjRecur(obj.getClass(), fieldName);
        if (field != null) {
            try {
                return (T) field.get(obj);
            } catch (IllegalAccessException ignored) {
            }
        }
        return null;
    }

    public static Class<?> classFromNames(String... classNames) {
        if (classNames != null && classNames.length > 0) {
            Class<?> cls;
            for (String s : classNames) {
                cls = classFromName(s);
                if (cls != null) {
                    return cls;
                }
            }
        }
        return null;
    }

    public static Class<?> classFromName(String className) {
        if (className != null && className.length() > 0) {
            try {
                Class<?> clazz = mObjectLruCache.get(className);
                //未查找到当前类缓存 并且 未查找过，去查找
                if (clazz == null && !mObjectSet.contains(className)) {
                    clazz = Class.forName(className);
                    //查找成功
                    mObjectLruCache.put(className, clazz);
                }
                return clazz;
            } catch (Exception e) {
                //查找失败 加入查找缓存队列，防止重复查找
                mObjectSet.add(className);
            }
        }
        return null;
    }

    /**
     * 是否是某些Class中的一种
     *
     * @param object     对象
     * @param classNames 某个Class 或 某些Class
     */
    public static boolean isInstance(Object object, String... classNames) {
        if (classNames != null && classNames.length > 0) {
            Class<?> cls;
            for (String className : classNames) {
                cls = classFromName(className);
                if (cls != null && cls.isInstance(object)) {
                    return true;
                }
            }
        }
        return false;
    }
}
